package micencats;
/*
 */

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.ImageIcon;
import javax.swing.JComponent;

/**
 */
public class MapEngine extends JComponent implements MouseMotionListener, MouseListener {
	static final long serialVersionUID = 0;
	
	// Map description
	int width, height;
	ImageIcon[][] map;
	ImageIcon[][] sprites;
	ImageIcon back;
	int cellWidth, cellHeight;
	
	// Cursor management
	int mouseI = -1, mouseJ = -1;
	Color cursor_color = Color.MAGENTA;
	Vector listeners = new Vector();
	
	/**
	 * Build a new display engine.
	 * @param width		Map width.
	 * @param height	Map height.
	 * @param back		Back image.
	 */
	public MapEngine(int width, int height, ImageIcon back) {
		
		// Initlize attributes
		this.width = width;
		this.height = height;
		this.back = back;
		map = new ImageIcon[width][height];
		sprites = new ImageIcon[width][height];		
		cellWidth = back.getIconWidth();
		cellHeight = back.getIconHeight();
		
		// Fill the map
		for(int i = 0; i < width; i++)
			for(int j = 0; j < height; j++)
				map[i][j] = back;
		
		// Graphics initialization
		setVisible(true);
		addMouseMotionListener(this);
		addMouseListener(this);
	}

	/**
	 * Add a listener for map events.
	 * @param listener	Listener to add.
	 */
	public void addMapListener(MapListener listener) {
		listeners.add(listener);
	}
	
	/**
	 * Remove a listener of map events.
	 * @param listener	Listener to remove.
	 */
	public void removeMapListener(MapListener listener) {
		listeners.remove(listener);
	}
	
	/**
	 * Change the displayed cell at the given position.
	 * @param i			Cell column index.
	 * @param j			Cell line index.
	 * @param img		Image to display.
	 */
	public void setCell(int i, int j, ImageIcon img) {
		map[i][j] = img;
		repaint(0, xForColumn(i), yForLine(j), cellWidth, cellHeight);
	}
	
	/**
	 * Cleat the displayed image at the given cell position (replaced by the back image).
	 * @param i		Cell column index.
	 * @param j		Cell line index.
	 */
	public void clearCell(int i, int j) {
		map[i][j] = back;
		repaint(0, xForColumn(i), yForLine(j), cellWidth, cellHeight);
	}
	
	/**
	 * Add a dynamic object to the display.
	 * @param i		Column position.
	 * @param j		Line position.
	 * @param img	Object image.
	 */
	public void setSprite(int i, int j, ImageIcon img) {
		sprites[i][j] = img;
		repaint(0, xForColumn(i), yForLine(j), cellWidth, cellHeight);
	}
	
	/**
	 * Clear the dynamic object at the given position.
	 * @param i		Column position.
	 * @param j		Line position.
	 */
	public void clearSprite(int i, int j) {
		sprites[i][j] = null;
		repaint(0, xForColumn(i), yForLine(j), cellWidth, cellHeight);
	}
	
	/**
	 * Convert pixel position to cell index for X.
	 * @param x	X value.
	 * @return	Cell column.
	 */
	private int jForX(int x) {
		return x / cellWidth;
	}
	
	/**
	 * Convert pixel position to cell index for X.
	 * @param y	Y value.
	 * @return	Cell line.
	 */
	private int iForY(int y) {
		return y / cellHeight;
	}
	
	/**
	 * Convert from cell column to X coordinate.
	 * @param i	Cell column.
	 * @return	X coordinate.
	 */
	private int xForColumn(int i) {
		return i * cellWidth;
	}
	
	/**
	 * Convert from cell line to Y coordinate.
	 * @param j	Cell line.
	 * @return	Y coordinate.
	 */
	private int yForLine(int j) {
		return j * cellHeight;
	}
	
	/* (non-Javadoc)
	 * @see java.awt.Component#getHeight()
	 */
	public int getHeight() {
		return height * cellHeight;
	}
	
	/* (non-Javadoc)
	 * @see java.awt.Component#getWidth()
	 */
	public int getWidth() {
		return width * cellWidth;
	}

	/**
	 * Redraw the cell at the given position.
	 * @param g	Graphics.
	 * @param i	Cell column.
	 * @param j	Cell line.
	 */
	private void paintCell(Graphics g, int i, int j) {
		
		// Display back
		map[i][j].paintIcon(this, g, xForColumn(i), yForLine(j));
		
		// Display dynamic object upon if needed
		if(sprites[i][j] != null)
			sprites[i][j].paintIcon(this, g, xForColumn(i), yForLine(j));
		
		// Draw cursor if needed
		if(mouseI == i && mouseJ == j) {
			g.setColor(cursor_color);
			g.drawRect(xForColumn(i), yForLine(j), cellWidth - 1, cellHeight - 1);
		}		
	}
	
	/**
	 * Paint the map in the given bounds.
	 * @param g		Used graphics.
	 * @param x		Repainted area X.
	 * @param y		Repainted area Y.
	 * @param w		Repainted area width.
	 * @param h		Repainted area height.
	 */
	private void paintIt(Graphics g, int x, int y, int w, int h) {
		int area_l, area_r, area_t, area_b; 
		
		// Transform coordinates
		area_l = jForX(x);
		area_t = iForY(y);
		area_r = jForX(x + w - 1);
		area_b = iForY(y + h - 1);
		
		// Redisplay erased cells
		for(int i = area_l; i <= area_r; i++)
			for(int j = area_t; j <= area_b; j++)
				paintCell(g, i, j);
	}
	
	
	/* (non-Javadoc)
	 * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
	 */
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		paintIt(g, 0, 0, getWidth(), getHeight());
	}
	
	
	/* (non-Javadoc)
	 * @see java.awt.Component#getPreferredSize()
	 */
	public Dimension getPreferredSize() {
		return new Dimension(getWidth(), getHeight());
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
	 */
	public void mouseDragged(MouseEvent e) {
		// Do nothing
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
	 */
	public void mouseMoved(MouseEvent e) {
		
		// Something has changed ?
		int new_i = jForX(e.getX());
		int new_j = iForY(e.getY());
		if(new_i == mouseI && new_j == mouseJ)
			return;
		
		// Yes, move and repaint
		if(mouseI >= 0)
			repaint(xForColumn(mouseI), yForLine(mouseJ), cellWidth, cellHeight);
		mouseI = new_i;
		mouseJ = new_j;
		repaint(xForColumn(mouseI), yForLine(mouseJ), cellWidth, cellHeight);
		
		// Alert listeners
		Iterator iter = listeners.iterator();
		while(iter.hasNext()) {
			MapListener listener = (MapListener)iter.next();
			listener.mouseMove(mouseI, mouseJ, e.getModifiers());
		}
	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
	 */
	public void mouseClicked(MouseEvent e) {
		Iterator iter = listeners.iterator();
		while(iter.hasNext()) {
			MapListener listener = (MapListener)iter.next();
			listener.mouseClick(mouseI, mouseJ, e.getButton(), e.getModifiers());
		}
	}

	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
	 */
	public void mouseEntered(MouseEvent e) {
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
	 */
	public void mouseExited(MouseEvent e) {
		repaint(xForColumn(mouseI), yForLine(mouseJ), cellWidth, cellHeight);
		mouseI = -1;
		mouseJ = -1;
	}
	
	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
	 */
	public void mousePressed(MouseEvent e) {
	}

	/* (non-Javadoc)
	 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
	 */
	public void mouseReleased(MouseEvent e) {
	}
}
